package com.miku.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.miku.common.BaseResponse;
import com.miku.common.ErrorCode;
import com.miku.common.ResultUtils;
import com.miku.constant.AiConstant;
import com.miku.exception.BusinessException;
import com.miku.exception.ThrowUtils;
import com.miku.manager.RedisLimterManager;
import com.miku.model.dto.text.BiToMdResponse;
import com.miku.model.dto.text.MeargeDto;
import com.miku.model.dto.text.TextByAiRequest;
import com.miku.model.entity.Chart;
import com.miku.model.entity.TextRecord;
import com.miku.model.entity.TextTask;
import com.miku.model.entity.User;
import com.miku.model.enums.TextStatusEnum;
import com.miku.mq.producer.MessageProducer;
import com.miku.service.CreditService;
import com.miku.service.TextRecordService;
import com.miku.service.TextTaskService;
import com.miku.service.UserService;
import com.miku.utils.SparkUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.miku.constant.AiConstant.CHAHE_KEY;
import static com.miku.constant.MessageConstant.BI_EXCHANGE;
import static com.miku.constant.MessageConstant.BI_TO_MD_ROUTING_KEY;

@Api(tags = "文本接口")
@RestController
@RequestMapping("/text")
@Slf4j
public class TextController {


    @Resource
    private UserService userService;

    @Resource
    private RedisLimterManager redisLimterManager;
    @Resource
    private TextTaskService textTaskService;
    @Resource
    private TextRecordService textRecordService;
    @Resource
    private MessageProducer messageProducer;
    @Resource
    private CreditService creditService;


    /**
     * 调用ai服务将根据笔记文本智能生成md格式的笔记
     * @param multipartFile
     * @param aiRequest
     * @param request
     * @return
     */
    @PostMapping("/generate")
    @ApiOperation("调用ai服务将根据笔记文本智能生成md格式的笔记")
    public BaseResponse<BiToMdResponse> biToMd(@RequestPart("file") MultipartFile multipartFile,
                                              TextByAiRequest aiRequest, HttpServletRequest request) {
        //笔迹名称
        String name = aiRequest.getName();
        //笔记类型
        String textType = aiRequest.getTextType();
        //校验
        ThrowUtils.throwIf(StringUtils.isBlank(textType), ErrorCode.PARAMS_ERROR, "目标不能为空");
        ThrowUtils.throwIf(StringUtils.isNotBlank(name) && name.length() > 100, ErrorCode.PARAMS_ERROR, "笔记名称名称不能超过100个字符");
        //校验文件
        String filename = multipartFile.getOriginalFilename();
        long fileSize = multipartFile.getSize();
        final long ONE_MB = 1024 * 1024L;
        //文件大小不能超过10MB
        ThrowUtils.throwIf(fileSize > 10 * ONE_MB, ErrorCode.PARAMS_ERROR, "文件大小不能超过10MB");
        //校验文件后缀
        String suffix = FileUtil.getSuffix(filename);
        //文本类型
        final List<String> suffixList = Arrays.asList("txt");
        ThrowUtils.throwIf(!suffixList.contains(suffix), ErrorCode.PARAMS_ERROR, "文件格式不正确");

        User loginUser = userService.getLoginUser(request);
        //限流判断-每个用户一个限流器
        redisLimterManager.doRateLimit(AiConstant.LIMIT_PREFIX_AI_TEXT + loginUser.getId().toString());
        //TODO:发送消息调用ai服务将笔记转为md的形式
        //保存任务
        TextTask textTask = new TextTask();
        textTask.setTextType(textType);
        textTask.setExecMessage("正在执行中...");
        textTask.setName(name);
        textTask.setUserId(loginUser.getId());
        textTask.setStatus(TextStatusEnum.RUNNING.getValue());
        boolean save = textTaskService.save(textTask);
        if (!save) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "保存失败");
        }
        //保存分割文章记录
        Integer size = saveTextRecord(multipartFile, textTask);

        //返回数据
        BiToMdResponse biToMdResponse = new BiToMdResponse();
        biToMdResponse.setId(textTask.getId());
        biToMdResponse.setSize(size);
        biToMdResponse.setExecMessage(textTask.getExecMessage());
        biToMdResponse.setStatus(textTask.getStatus());
        biToMdResponse.setName(textTask.getName());
        biToMdResponse.setUserId(textTask.getUserId());
        biToMdResponse.setTextType(textTask.getTextType());
        return ResultUtils.success(biToMdResponse);
    }

    /**
     * 合并文本接口
     * @param meargeDto
     * @param request
     * @return
     */
    @PostMapping("/meagrge")
    @ApiOperation("合并文本接口")
    public BaseResponse<Long> mearge(@RequestBody MeargeDto meargeDto, HttpServletRequest request){
        //检测参数
        Long textTaskId = meargeDto.getTextTaskId();
        Integer size = meargeDto.getSize();
        ThrowUtils.throwIf(meargeDto==null|| textTaskId ==null,ErrorCode.PARAMS_ERROR,"参数错误");
      //查询数据库
      StringBuilder sb=new StringBuilder();
        for (Long i = textTaskId; i < size+textTaskId; i++) {
            TextRecord textRecord = textRecordService.getOne(Wrappers.<TextRecord>lambdaQuery()
                    .eq(TextRecord::getTextTaskId, i)
                    .eq(TextRecord::getStatus, TextStatusEnum.SUCCEEd.getValue()));
            if (textRecord==null){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"参数错误");
            }
            //合并完把之前的记录删除
            boolean remove = textRecordService.remove(Wrappers.<TextRecord>lambdaQuery()
                    .eq(TextRecord::getTextTaskId, i));
            ThrowUtils.throwIf(!remove,ErrorCode.PARAMS_ERROR,"删除失败");
            sb.append(textRecord.getTextContent());
        }
        //插入
        boolean update = textTaskService.update(Wrappers.<TextTask>lambdaUpdate()
                .eq(TextTask::getId, textTaskId)
                .set(TextTask::getGenTextContent, sb.toString())
                .set(TextTask::getStatus, TextStatusEnum.SUCCEEd.getValue())
                .set(TextTask::getExecMessage, "合并完成"));
        if (!update){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"合并失败");
        }

        return ResultUtils.success(textTaskId);
    }



    /**
     * 根据 id 获取
     *
     * @param id
     * @return
     */
    @GetMapping("/get")
    @ApiOperation("根据 id 获取文章信息")
    public BaseResponse<TextTask> getChartVOById(long id, HttpServletRequest request) {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        TextTask textTask = textTaskService.getById(id);

        return ResultUtils.success(textTask);
    }

    /**
     * 保存文本记录
     * @param multipartFile
     * @param textTask
     */
    private Integer saveTextRecord(MultipartFile multipartFile, TextTask textTask) {
        //处理文章,将文章分为一块一块的
        List<String> textList = SparkUtils.readerFile(multipartFile);
        List<Long> ids=new ArrayList<>();
        for (int i = 0; i < textList.size(); i++) {
            Long id= textTask.getId()+i;
                ids.add(id);
            //保存到记录表
            TextRecord textRecord = new TextRecord();
            textRecord.setTextContent(textList.get(i));
            textRecord.setTextTaskId(id);
            textRecord.setStatus(TextStatusEnum.RUNNING.getValue());
            textRecord.setExecMessage("正在执行中...");
            boolean saved = textRecordService.save(textRecord);
            if (!saved) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "保存失败");
            }
        }
        //发送消息 需要发送 textTaskId-文本任务id textId-任务id 分成文章的集合大小 textSize
        for (Long textTaskId : ids) {
            //发送消息
            messageProducer.sendMessageToBi(BI_EXCHANGE,BI_TO_MD_ROUTING_KEY,textTask.getId(), textTaskId,textList.size());
        }
        //消费要消耗积分
        Boolean credits = creditService.consumeCredits(textTask.getUserId(), -1L);
        if (!credits){
             throw new BusinessException(ErrorCode.PARAMS_ERROR,"积分不足");
        }
        return textList.size();

    }
}
